#include "../include/command.h"
#pragma warning(disable : 4996)

MemoryNode<dword> CodeMem[65536];
MemoryNode<byte> DataMem[65536];

extern bool RUNNING, ERROR, Zero, Sign;

extern word CodeH, CodeL;
extern IREG intRegs[128];
extern FREG floatReg[64];
extern DREG doublReg[64];

dword loader()
{
	dword code = 0;
	if (!RUNNING)
		return -1;
	code = CodeMem[CodeH].data[CodeL];
	if (CodeL == 0xffff)
		CodeH += 1;
	CodeL += 1;
	return code;
}
void selector(dword code)
{
	if (code == -1)
	{
		printf("Program has ended.\n");
		exit(1);
	}
	switch (code >> 29)
	{
	case Spec:
		TypeS((CodeS *)(&code));
		break;
	case Regs:
		TypeR((CodeR *)(&code));
		break;
	case RAMs:
		TypeLI((CodeLI *)(&code));
		break;
	case Float:
		TypeF((CodeF *)(&code));
		break;
	default:
		exit(1);
	}
}
void TypeS(CodeS *code)
{
	FILE *io;
	char *filename;
	switch (code->name & 0x1f)
	{
	case 0x00:
		break;
	case 0x01:
		DataMem[intRegs[DI].Block[1]].data[intRegs[DI].Block[0]] = getchar();
		break;
	case 0x02:
		putchar(DataMem[intRegs[SI].Block[1]].data[intRegs[SI].Block[0]]);
		break;
	case 0x03:
		filename = (char *)malloc(sizeof(char) * code->arg1 + 1);
		if (filename == NULL)
			exit(1);
		memcpy(filename, DataMem[intRegs[SI].Block[1]].data + intRegs[SI].Block[0], code->arg1);
		filename[code->arg1] = '\0';
		io = fopen(filename, "rb");
		fread(DataMem[intRegs[DI].Block[1]].data + intRegs[DI].Block[0], 1, code->arg1, io);
		free(filename);
		break;
	case 0x04:
		filename = (char *)malloc(sizeof(char) * code->arg1 + 1);
		if (filename == NULL)
			exit(1);
		memcpy(filename, DataMem[intRegs[DI].Block[1]].data + intRegs[DI].Block[0], code->arg1);
		filename[code->arg1] = '\0';
		io = fopen(filename, "wb");
		fwrite(DataMem[intRegs[SI].Block[1]].data + intRegs[SI].Block[0], 1, code->arg1, io);
		free(filename);
		break;
	case 0x05:
		RUNNING = false;
		break;
	case 0x10:
		CodeMem[intRegs[DI].Block[1]].data = new dword[intRegs[DI].Block[0]];
		CodeMem[intRegs[DI].Block[1]].size = intRegs[DI].Block[0];
		break;
	case 0x11:
		DataMem[intRegs[DI].Block[1]].data = new byte[intRegs[DI].Block[0]];
		DataMem[intRegs[DI].Block[1]].size = intRegs[DI].Block[0];
		break;
	case 0x12:
		free(CodeMem[intRegs[DI].Block[1]].data);
		break;
	case 0x13:
		free(DataMem[intRegs[DI].Block[1]].data);
		break;
	case 0x18:
		if (intRegs[Count].qvalue > 0)
		{
			CodeH = intRegs[code->arg1].Block[1];
			CodeL = code->arg2;
			intRegs[Count].qvalue -= 1;
		}
		break;
	case 0x19:
		memcpy(&CodeL, DataMem[intRegs[Stack].Block[1]].data + intRegs[Stack].Block[0], 2);
		memcpy(&CodeH, DataMem[intRegs[Stack].Block[1]].data + intRegs[Stack].Block[0] + 2, 2);
		intRegs[Stack].qvalue += 4;
		break;
	default:
		exit(1);
	}
}
void TypeR(CodeR *code)
{
	switch (code->name & 0x1f)
	{
	case 0x00:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue + intRegs[code->r2].qvalue;
		checkValueQ(intRegs[code->rd].qvalue);
		break;
	case 0x01:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue - intRegs[code->r2].qvalue;
		checkValueQ(intRegs[code->rd].qvalue);
		break;
	case 0x02:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue + code->r2;
		checkValueQ(intRegs[code->rd].qvalue);
		break;
	case 0x03:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue - code->r2;
		checkValueQ(intRegs[code->rd].qvalue);
		break;
	case 0x04:
		intRegs[code->rd] = intRegs[code->r1];
		break;
	case 0x05:
		intRegs[code->rd].qvalue += 1;
		break;
	case 0x06:
		intRegs[code->rd].qvalue -= 1;
		break;
	case 0x07:
		intRegs[code->rd].qvalue = -(long long)(intRegs[code->r1].qvalue);
		break;
	case 0x0f:
		intRegs[code->rd].qvalue = 0;
		break;
	case 0x10:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue & intRegs[code->r2].qvalue;
		break;
	case 0x11:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue | intRegs[code->r2].qvalue;
		break;
	case 0x12:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue ^ intRegs[code->r2].qvalue;
		break;
	case 0x13:
		intRegs[code->rd].qvalue = ~intRegs[code->r1].qvalue;
		break;
	case 0x14:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue << code->r2;
		break;
	case 0x15:
		intRegs[code->rd].qvalue = intRegs[code->r1].qvalue >> code->r2;
		break;
	case 0x16:
		checkValueQ(intRegs[code->r1].qvalue - intRegs[code->r2].qvalue);
		break;
	}
}
void TypeLI(CodeLI *code)
{
	switch (code->name & 0x1f)
	{
	case 0x00:
		intRegs[code->regd].qvalue = DataMem[intRegs[Data].Block[1]].data[code->address];
		break;
	case 0x01:
		memcpy(&intRegs[code->regd].qvalue, DataMem[intRegs[Data].Block[1]].data + code->address, 2);
		break;
	case 0x02:
		memcpy(&intRegs[code->regd].qvalue, DataMem[intRegs[Data].Block[1]].data + code->address, 4);
		break;
	case 0x03:
		memcpy(&intRegs[code->regd].qvalue, DataMem[intRegs[Data].Block[1]].data + code->address, 8);
		break;
	case 0x04:
		intRegs[code->regd].qvalue = DataMem[intRegs[code->address >> 8].Block[1]].data[intRegs[code->address >> 8].Block[0] + (code->address & 0xff)];
		break;
	case 0x05:
		memcpy(&intRegs[code->regd].qvalue, DataMem[intRegs[code->address >> 8].Block[1]].data + intRegs[code->address >> 8].Block[0] + (code->address & 0xff), 2);
		break;
	case 0x06:
		memcpy(&intRegs[code->regd].qvalue, DataMem[intRegs[code->address >> 8].Block[1]].data + intRegs[code->address >> 8].Block[0] + (code->address & 0xff), 4);
		break;
	case 0x07:
		memcpy(&intRegs[code->regd].qvalue, DataMem[intRegs[code->address >> 8].Block[1]].data + intRegs[code->address >> 8].Block[0] + (code->address & 0xff), 8);
		break;
	case 0x08:
		DataMem[intRegs[Data].Block[1]].data[code->address] = (byte)intRegs[code->regd].qvalue;
		break;
	case 0x09:
		memcpy(DataMem[intRegs[Data].Block[1]].data + code->address, &intRegs[code->regd].qvalue, 2);
		break;
	case 0x0a:
		memcpy(DataMem[intRegs[Data].Block[1]].data + code->address, &intRegs[code->regd].qvalue, 4);
		break;
	case 0x0b:
		memcpy(DataMem[intRegs[Data].Block[1]].data + code->address, &intRegs[code->regd].qvalue, 8);
		break;
	case 0x0c:
		DataMem[intRegs[code->address >> 8].Block[1]].data[intRegs[code->address >> 8].Block[0] + (code->address & 0xff)] = (byte)intRegs[code->regd].qvalue;
		break;
	case 0x0d:
		memcpy(DataMem[intRegs[code->address >> 8].Block[1]].data + intRegs[code->address >> 8].Block[0] + (code->address & 0xff), &intRegs[code->regd].qvalue, 2);
		break;
	case 0x0e:
		memcpy(DataMem[intRegs[code->address >> 8].Block[1]].data + intRegs[code->address >> 8].Block[0] + (code->address & 0xff), &intRegs[code->regd].qvalue, 4);
		break;
	case 0x0f:
		memcpy(DataMem[intRegs[code->address >> 8].Block[1]].data + intRegs[code->address >> 8].Block[0] + (code->address & 0xff), &intRegs[code->regd].qvalue, 8);
		break;
	case 0x10:
		intRegs[code->regd].Block[3] = code->address;
		break;
	case 0x11:
		intRegs[code->regd].Block[2] = code->address;
		break;
	case 0x12:
		intRegs[code->regd].Block[1] = code->address;
		break;
	case 0x13:
		intRegs[code->regd].Block[0] = code->address;
		break;
	case 0x18:
		CodeH = intRegs[code->regd].Block[1];
		CodeL = code->address;
		break;
	case 0x19:
		if (Zero)
		{
			CodeH = intRegs[code->regd].Block[1];
			CodeL = code->address;
		}
		break;
	case 0x1a:
		if (!Sign)
		{
			CodeH = intRegs[code->regd].Block[1];
			CodeL = code->address;
		}
		break;
	case 0x1b:
		if (Zero || !Sign)
		{
			CodeH = intRegs[code->regd].Block[1];
			CodeL = code->address;
		}
		break;
	case 0x1c:
		if (Sign)
		{
			CodeH = intRegs[code->regd].Block[1];
			CodeL = code->address;
		}
		break;
	case 0x1d:
		if (Zero || Sign)
		{
			CodeH = intRegs[code->regd].Block[1];
			CodeL = code->address;
		}
		break;
	case 0x1e:
		if (!Zero)
		{
			CodeH = intRegs[code->regd].Block[1];
			CodeL = code->address;
		}
		break;
	case 0x1f:
		memcpy(DataMem[intRegs[Stack].Block[1]].data + intRegs[Stack].Block[0], &CodeH, 2);
		intRegs[Stack].qvalue -= 2;
		memcpy(DataMem[intRegs[Stack].Block[1]].data + intRegs[Stack].Block[0] + 2, &CodeL, 2);
		intRegs[Stack].qvalue -= 2;
		CodeH = intRegs[code->regd].Block[1];
		CodeL = code->address;
		break;
	default:
		exit(1);
	}
}
void TypeF(CodeF *code)
{
	if ((code->arg3 <= 0x7f) && (code->name != 0x70))
		exit(1);
	switch (code->name & 0x1f)
	{
	case 0x00:
		if (code->arg3 < 0xc0)
			floatReg[code->arg3 & 0x3f] = floatReg[code->arg1 & 0x3f] + floatReg[code->arg2 & 0x3f];
		else
			doublReg[code->arg3 & 0x3f] = doublReg[code->arg1 & 0x3f] + doublReg[code->arg2 & 0x3f];
		break;
	case 0x01:
		if (code->arg3 < 0xc0)
			floatReg[code->arg3 & 0x3f] = floatReg[code->arg1 & 0x3f] - floatReg[code->arg2 & 0x3f];
		else
			doublReg[code->arg3 & 0x3f] = doublReg[code->arg1 & 0x3f] - doublReg[code->arg2 & 0x3f];
		break;
	case 0x02:
		if (code->arg3 < 0xc0)
			floatReg[code->arg3 & 0x3f] = floatReg[code->arg1 & 0x3f] * floatReg[code->arg2 & 0x3f];
		else
			doublReg[code->arg3 & 0x3f] = doublReg[code->arg1 & 0x3f] * doublReg[code->arg2 & 0x3f];
		break;
	case 0x03:
		if (code->arg3 < 0xc0)
			floatReg[code->arg3 & 0x3f] = floatReg[code->arg1 & 0x3f] / floatReg[code->arg2 & 0x3f];
		else
			doublReg[code->arg3 & 0x3f] = doublReg[code->arg1 & 0x3f] / doublReg[code->arg2 & 0x3f];
		break;
	case 0x04:
		if (code->arg3 == 0)
			checkValueF(floatReg[code->arg3 & 0x3f]);
		else
			checkValueD(doublReg[code->arg3 & 0x3f]);
		break;
	case 0x08:
		if (code->arg3 < 0xc0)
			memcpy(&floatReg[code->arg3 & 0x3f], DataMem[intRegs[code->arg1].Block[1]].data + intRegs[code->arg1].Block[0] + code->arg2, 4);
		else
			memcpy(&doublReg[code->arg3 & 0x3f], DataMem[intRegs[code->arg1].Block[1]].data + intRegs[code->arg1].Block[0] + code->arg2, 8);
		break;
	case 0x09:
		if (code->arg3 < 0xc0)
			memcpy(DataMem[intRegs[code->arg1].Block[1]].data + intRegs[code->arg1].Block[0] + code->arg2, &floatReg[code->arg3 & 0x3f], 4);
		else
			memcpy(DataMem[intRegs[code->arg1].Block[1]].data + intRegs[code->arg1].Block[0] + code->arg2, &doublReg[code->arg3 & 0x3f], 8);
		break;
	case 0x10:
		switch (code->arg2)
		{
		case 0x00:
			if ((code->arg2 < 0x80) && (code->arg3 > 0x80))
				break;
			else if (code->arg1 < 0xc0)
				intRegs[code->arg3 & 0x7f].qvalue = (qword)(floatReg[code->arg1 & 0x3f]);
			else
				intRegs[code->arg3 & 0x7f].qvalue = (qword)(doublReg[code->arg1 & 0x3f]);
			break;
		case 0x01:
			if (((code->arg3 > 0x80) && (code->arg3 < 0xc0)) && ((code->arg1 > 0x80) && (code->arg1 < 0xc0)))
				break;
			else if (code->arg1 < 0x80)
				floatReg[code->arg3 & 0x3f] = (float)(intRegs[code->arg1 & 0x3f].qvalue);
			else
				floatReg[code->arg3 & 0x3f] = (float)(doublReg[code->arg1 & 0x3f]);
			break;
		case 0x02:
			if ((code->arg3 > 0xc0) && (code->arg1 > 0xc0))
				break;
			else if (code->arg1 < 0x80)
				doublReg[code->arg3 & 0x3f] = (double)(intRegs[code->arg1].qvalue);
			else
				doublReg[code->arg3 & 0x3f] = (double)(floatReg[code->arg1 & 0x3f]);
		}
	}
}